package com.neuronbit.businessflow.runtime;

import com.neuronbit.businessflow.runtime.exec.ExecutedFlowInstance;
import com.neuronbit.businessflow.runtime.exec.FlowExecutionStatus;
import com.neuronbit.businessflow.runtime.exec.LockStatus;
import com.neuronbit.businessflow.runtime.lifecycle.FlowLifecycleHook;
import com.neuronbit.businessflow.runtime.repo.BusinessFlowRepository;

import java.sql.SQLException;
import java.time.LocalDateTime;

import static com.neuronbit.businessflow.runtime.exec.FlowExecutionStatus.INIT;
import static com.neuronbit.businessflow.runtime.exec.FlowExecutionStatus.START;
import static com.neuronbit.businessflow.runtime.exec.LockStatus.LOCKED;

/**
 * The interface Flow lifecycle hook.
 */
public class FlowLifecycleHookImpl implements FlowLifecycleHook {
    private final BusinessFlowRepository repository;

    public FlowLifecycleHookImpl(BusinessFlowRepository repository) {
        this.repository = repository;
    }

    /**
     * Support boolean.
     *
     * @param context the context
     * @param flow    the flow
     * @return the boolean
     */
    public boolean support(FlowContext context, Flow flow) {
        return true;
    }

    /**
     * flow 是否可重入
     * 如果可重入，则通过、flow id+入参找到上次执行的记录
     * 1. 加载context
     * 2. 加载 step 执行记录
     * 3.
     *
     * @param context the context
     * @param flow    the flow
     */
    public void onStart(FlowContext context, Flow flow) {
        ExecutedFlowInstance instance = new ExecutedFlowInstance();
        instance.setExecutionId(context.getExecutionId());
        instance.setContext(GsonUtil.toJson(context));
        instance.setStatus(START);
        instance.setLockStatus(LOCKED);
        instance.setEndTime(LocalDateTime.now());
        try {
            this.repository.updateFlowExecutionByExecutionId(instance, INIT, LOCKED);
        } catch (SQLException e) {
            //FIXME shunyun 2024/7/24: 这里是否应该抛出异常？
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新 flow 执行记录：状态=执行完成，输出，执行后的 context，执行耗时
     *
     * @param context the context
     * @param flow    the flow
     */
    public void onEnd(FlowContext context, Flow flow) {
        ExecutedFlowInstance instance = new ExecutedFlowInstance();
        instance.setExecutionId(context.getExecutionId());
        instance.setContext(GsonUtil.toJson(context));
        instance.setStatus(FlowExecutionStatus.END);
        instance.setLockStatus(LockStatus.UNLOCKED);
        instance.setEndTime(LocalDateTime.now());
        try {
            this.repository.updateFlowExecutionByExecutionId(instance, START, LOCKED);
        } catch (SQLException e) {
            //FIXME shunyun 2024/7/24: 这里是否应该抛出异常？
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新 flow 执行记录：状态=执行失败，异常信息，异常堆栈，执行耗时，累加执行次数
     *
     * @param context the context
     * @param flow    the flow
     * @param e       the e
     */
    public void onError(FlowContext context, Flow flow, Exception e) {
        ExecutedFlowInstance instance = new ExecutedFlowInstance();
        instance.setExecutionId(context.getExecutionId());
        instance.setContext(GsonUtil.toJson(context));
        instance.setStatus(FlowExecutionStatus.ERROR);
        instance.setLockStatus(LockStatus.UNLOCKED);
        instance.setEndTime(LocalDateTime.now());
        instance.setErrorMessage(e.getMessage());
        try {
            this.repository.updateFlowExecutionByExecutionId(instance, START, LOCKED);
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    public void onPause(FlowContext context, Flow flow) {
        ExecutedFlowInstance instance = new ExecutedFlowInstance();
        instance.setExecutionId(context.getExecutionId());
        instance.setContext(GsonUtil.toJson(context));
        instance.setStatus(FlowExecutionStatus.PAUSED);
        instance.setLockStatus(LockStatus.UNLOCKED);
        instance.setEndTime(LocalDateTime.now());
        try {
            this.repository.updateFlowExecutionByExecutionId(instance, START, LOCKED);
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
    }
}
